# write by hasaki
# first edit on 2018/12/11
# last change on 2019/07/14
# 正经的gateio交易接口抽象
import os
import sys
sys.path.append(os.getcwd())

import json
import threading
from queue import Queue
from time import sleep
import socket
import time
from datetime import datetime

from hashakisea.api.digital.gateio import GateIO
from hashakisea.api.digital.gate_ws import GateWebsocket

class GateGateway:
    def __init__(self):
        self.api=GateIO()
        self.ws=GateWebsocket()

        self.data_queue=Queue(maxsize=10)   # 消息队列
        self.socket=socket.socket()         # socket接口
        self.apikey=None                    # gate的api key
        self.scretekey=None                 # gate的密钥
        self.key_path=None                  # 用户账户设置json路径
        self.exchange_name=None             # 交易所名称
        self.coins=None                     # 默认订阅品种,['eos_usdt']
        self.data_port=None                 # 从交易所获取的行情数据发送到的端口，默认端口6666
        self.mode=None                      # 模式的选择
        self.__activate=False               # 是否循环查询

    # ----------------------------------------------------------------------------
    # 参数读取部分
    def getKey(self):
        '''获取用户的key'''
        with open(self.key_path,'r') as f:
            key=json.load(f)
            self.apikey=key['api_key']
            self.scretekey=key['screte_key']
            self.exchange_name=key['exchange']
            self.coins=key['coins']
            self.api.setKey(self.apikey,self.scretekey)

    def getJsonPath(self):
        '''获取json路径'''
        orgin_path=os.getcwd()
        json_setting_path=orgin_path+'/'+'userSetting'+'/'+'gateio.json'
        self.key_path=json_setting_path
    
    def settingPort(self,data_port=6666):
        '''设定数据发送端口，行情数据会发送到指定的端口'''
        self.data_port=data_port
    
    def settingMode(self,mode='tick'):
        '''mode模式选择，tick模式，bar模式，深度模式'''
        self.mode=mode

    # -----------------------------------------------------------------------------
    # 获取行情推送
    def startTick(self):
        '''不断向交易所发送请求，获取指定品种的tick数据'''
        while 1:
            for coin in self.coins:
                try:
                    data=self.api.ticker(coin)
                except Exception as error:
                    now=datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                    print('时间:{} 请求tick行情发生了错误:{}'.format(now,error))
                    sleep(10)
                    continue
                data['target_symbol']=coin
                temp={'data':data,'type':'tick'}
                self.data_queue.put(temp)
            
            sleep(2)

    def startDepth(self):
        '''不断向交易所发送请求，获取指定品种的深度数据'''
        while 1:
            for coin in self.coins:
                try:
                    depth=self.api.orderBook(coin)
                except Exception as error:
                    now=datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                    print('时间:{} 请求深度行情发生了错误:{}'.format(now,error))
                    sleep(10)
                    continue
                depth['target_symbol']=coin
                temp={'data':depth,'type':'depth'}
                self.data_queue.put(temp)
            
            sleep(2)
    
    def startBars(self,freqency='1min'):
        '''向交易所发送请求，获取指定品种的周期K线数据,frequency='1min' '''
        if freqency:
            for coin in self.coins:
                origin_data =self.api.kLine(coin,sec=60,hour=24)
                k_line={coin:self.dealTheKline(origin_data)}
                temp={'data':k_line,'type':'kline'}
                self.data_queue.put(temp)
        else:
            for coin in self.coins:
                k_line=self.api.kLine(coin)
                temp={'data':k_line,'type':'kline'}
                self.data_queue.put(temp)

    # -----------------------------------------------------------------------
    # websocket的行情推送，websocket推送的行情与rest请求的结果是不一样的
    def WsstartTick(self):
        pass
    
    def WsstartDepth(self):
        pass
    
    def WsstartBars(self):
        ''''从gateio的websocket获取K线数据'''
        self.ws.start()
        self.ws.futuresKline('EOS_USD','1m')
        self.ws.futuresKline('EOS_USD','1h')
        while 1:
            if self.__activate:
                try:
                    text_str=self.ws.run()
                except:
                    errortype,errorinfo,tb=sys.exc_info()
                    print('连接gateio交易所发生了错误 : {}'.format(str(errorinfo)))
                    sleep(10)
                    self.reconnect_ws_bar()
                    continue
                if not text_str:
                    self.reconnect_ws_bar()
                text=self.unpack(text_str)
                if 'status' in text['result']:
                    continue
                elif 'n' in text['result'][0]:
                    data={}
                    data['time']= time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(text['result'][0]['t']))
                    data['close']=float(text['result'][0]['c'])
                    data['high']=float(text['result'][0]['h'])
                    data['low']=float(text['result'][0]['l'])
                    data['open']=float(text['result'][0]['o'])
                    data['volume']=520.0
                    data['name']=text['result'][0]['n'].replace('_EOS_USD','')
                    temp={'data':data,'type':'bar'}
                    self.data_queue.put(temp)
                else:
                    print('获取bar，返回的数据没有对上',text,type(text))
    
    def reconnect_ws_bar(self):
        '''重连交易所的ws,防止频繁重连，这里睡5秒再重连'''
        print('gateio websocket发生了重连')
        sleep(5)
        self.ws.reconnect()
        self.ws.futuresKline('EOS_USD','1m')
        self.ws.futuresKline('EOS_USD','1h')
        
    # -----------------------------------------------------------------------
    # 账户信息的获取
    def getPos(self):
        '''获取所有订阅品种的持仓,pos={'EOS':'12'} '''
        # TODO : 需要把字段eos_usdt转换成大写的EOS
        pos={}
        for coin in self.coins:
            data=self.api.balances()['available'][coin]
            pos[coin]=float(data)
            temp={'data':pos,'type':'pos'}
            self.data_queue.put(temp)
    
    # 获取用户现金
    def getCash(self):
        '''获取用户还有多少现金'''
        cash=self.api.balances()['available']['USDT']
        temp={'data':float(cash),'type':'cash'}
        self.data_queue.put(temp)

    # ---------------------------------------------------------------
    # 通信相关
    def settingSocket(self):
        '''对socket进行设置'''
        host='127.0.0.1'
        self.socket.bind((host,self.data_port))
        self.socket.listen(10)
        self.socket.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
 
        self.socket.setsockopt(
            socket.SOL_SOCKET,
            socket.SO_SNDBUF,
            4096
            )
        self.socket.setsockopt(
            socket.SOL_SOCKET,
            socket.SO_RCVBUF,
            4096
            )
    
    # ----------------------------------------------------------------
    # 线程处理部分,运行部分
    def start(self):
        '''一些启动相关的设置'''
        self.__activate=True
        self.settingPort()
        self.settingSocket()

    #json 序列化
    def unpack(self,data):
        return json.loads(data)

    # 把交易所返回的K线数据进行处理
    def dealTheKline(self,origin_data):
        '''进来原始数据，返回一个K线列表数据结构'''
        K_line_data=origin_data['data']
        temp=[]
        for j in K_line_data:
            temp.append({'time':j[0],'volume':float(j[1]),'open':float(j[2]),'high':float(j[3]),'low':float(j[4]),'close':float(j[5])})
        return temp

    # 停止功能、
    def stop(self):
        '''一些关闭的相关设置'''
        self.__activate=False
        self.socket.close()

    # 启动
    def run(self):
        '''线程启动部分'''
        self.getJsonPath()
        self.getKey()
        self.settingPort()
        self.startBars()              # 要启动的K线，引擎那边就要做好专门对付启动历史数据的功能，现在还没有完成

        client,addr=self.socket.accept()
        t1=threading.Thread(target=self.startTick)
        t2=threading.Thread(target=self.startDepth)
        t3=threading.Thread(target=self.WsstartBars)
        t1.start()
        t2.start()
        t3.start()

        while self.__activate:
            while not self.data_queue.empty():
                data=self.data_queue.get()
                temp=json.dumps(data).encode('utf8')
                client.sendall(temp)
